//
// Created by zhaoyang on 2021/3/11.
//

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/time.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <dlfcn.h>
#include <sys/ioctl.h>
#include <string.h>
#include <time.h>
#define DIGEST_SIZE     32
#define EINVAL 1
#define ENOMEM 2
typedef unsigned char         BYTE;
typedef unsigned short int  UINT16;
typedef unsigned int        UINT32;
typedef unsigned long int   UINT64;
typedef unsigned short int    WORD;
typedef unsigned int         DWORD;

BYTE * privkey = NULL;
unsigned long  privkey_len;
BYTE * pubkey = NULL;
BYTE ExBuf[DIGEST_SIZE*32];

int TCM_ExCreateSm2Key(BYTE ** privkey,int * privkey_len,BYTE ** pubkey)
{
    int ret=0;
    int i;

    printf("Begin ex Create sm2 key:\n");

    BYTE prikey[DIGEST_SIZE*2];
    BYTE pubkey_XY[64];
    unsigned long prilen=DIGEST_SIZE*2;

    ret=GM_GenSM2keypair(prikey,&prilen,pubkey_XY);
    if(ret!=0)
        return -EINVAL;
    *privkey_len=prilen;

    *privkey=malloc(prilen);
    if(*privkey==NULL)
        return -ENOMEM;
    memcpy(*privkey,prikey,prilen);
    *pubkey=malloc(64);
    if(*pubkey==NULL)
        return -ENOMEM;
    memcpy(*pubkey,pubkey_XY,64);
    return 0;
}
int sm2Encrypt(BYTE * pubkey,BYTE * output,int * outlen,BYTE * in, int inlen)
{
	if(pubkey==NULL){
		printf("pubkey is empty\n");
		exit(-1);
	}
	int ret = -1;
	*outlen = inlen + 65 + 32 + 4;
	
	ret = GM_SM2Encrypt(output,outlen,in,inlen,pubkey,strlen(pubkey));
	if(ret!=0){
		printf("sm2 encrypt is failed\n");
		exit(-1);
	}
	
	return ret;
}

int sm2Decrypt(BYTE * privatekey,BYTE * output,unsigned long *outputlen,BYTE * input,unsigned long inputlen)
{
    if(privatekey==NULL){
        printf("privatekey is empty!\n");
        exit(-1);
    }
    int ret=1;
    ret = GM_SM2Decrypt(output,outputlen,input,inputlen,privatekey,strlen(privatekey));
    if(ret!=0){
         printf("decrypt error!\n");
         exit(-1);
    } 
    return ret;
}
int makerandomstring(int length,BYTE ** string)
{	
	srand((unsigned)time(NULL));
	int i = 0;
	BYTE * randomstring = (BYTE*)malloc(sizeof(BYTE)*length);
	for(i = 0 ; i < length-1 ;i++){
		randomstring[i]=rand()%16;			
	}
    *string=randomstring;
	return 0;
}
int print_hex_data(int length,BYTE * string)
{
	int i = 0;
	for(i = 0 ; i<length;i++)
	{
		if(i%16==0){
			printf("\n");
		}
		printf("%02x ",string[i]);	
	}
	printf("\n");
	return 0;
}

int main()
{
    int ret = 1;
    ret =TCM_ExCreateSm2Key(&privkey, &privkey_len, &pubkey);
    if(ret ==0){
        printf("create sm2 key successfully\n");
	printf("privkey:\n");
	print_hex_data(privkey_len,privkey);
	printf("pubkey:\n");
	print_hex_data(64,pubkey);
	printf("length:\n");
	printf("%u\n",privkey_len);
    }
    else{
        printf("failed to create sm2 key \n");
        return -EINVAL;
    }
    BYTE * signstring;
    BYTE * signdata;

	BYTE SignBuf[DIGEST_SIZE*32];
    UINT64 signlen=DIGEST_SIZE*16;
   	BYTE UserID[DIGEST_SIZE];
   	unsigned long lenUID = DIGEST_SIZE;
   	memset(UserID, 'A', 32);
    makerandomstring(128,&signstring);

    memcpy(ExBuf,signstring,128);


	ret=GM_SM2Sign(SignBuf,&signlen,
		ExBuf,64,
		UserID,lenUID,
		privkey,privkey_len);	

    printf("now show the sign data:\n");
    print_hex_data(signlen,SignBuf);

   	lenUID = DIGEST_SIZE;
   	memset(UserID, 'A', 32);
    ret=GM_SM2VerifySig(SignBuf,signlen,
        ExBuf,64,
		UserID,lenUID,
        pubkey,64);
    free(signstring);
    BYTE * instring ;
    printf("now make random data:\n");
    makerandomstring(128,&instring);
    printf("now show the random data:\n");
    print_hex_data(128,instring);
    BYTE * encryptdata = (BYTE*)malloc(sizeof(BYTE)*(512));
    UINT64  encryptdatalength = 128;
    sm2Encrypt(pubkey,encryptdata,&encryptdatalength,instring,128);
    printf("now show the encrypt data:\n");
    print_hex_data(encryptdatalength,encryptdata);
    free(instring);

    BYTE * decryptdata=(BYTE*)malloc(sizeof(BYTE)*512);
    UINT64 outputlen = 512;
    ret = sm2Decrypt(privkey,decryptdata,&outputlen,encryptdata,encryptdatalength);
    printf("now show the decrypt data:\n");
    print_hex_data(outputlen,decryptdata);
    free(decryptdata);
    free(encryptdata);

    return 0;
}
